#include "net.h"
#include "ip.h"
#include "ethernet.h"
#include "arp.h"
#include "icmp.h"

/**
 * @brief 处理一个收到的数据包
 * 
 * @param buf 要处理的数据包
 * @param src_mac 源mac地址
 */
void ip_in(buf_t *buf, uint8_t *src_mac)
{
    // TO-DO FIXME
    // 数据报合法性检测
    if(buf->len<sizeof(ip_hdr_t))
        return ;
    ip_hdr_t *iphdr = (ip_hdr_t *) buf->data;
    uint16_t hdr_len = iphdr->hdr_len * IP_HDR_LEN_PER_BYTE;
    uint16_t total_len = swap16(iphdr->total_len16);

    if(iphdr->version!=IP_VERSION_4 || total_len>buf->len || hdr_len<sizeof(ip_hdr_t))
        return ;

    // 检验和检错
    uint16_t src_checksum = iphdr->hdr_checksum16;
    iphdr->hdr_checksum16 = 0;
    if(checksum16((uint16_t *)buf->data, hdr_len)!=src_checksum)
        return ;
    iphdr->hdr_checksum16 = src_checksum;
    
    // 检查目的ip是否与本机ip一致
    if(memcmp(iphdr->dst_ip, net_if_ip, NET_IP_LEN)!=0)
        return ;

    // 检查是否需要rm_padding
    if(buf->len>total_len)
        buf_remove_padding(buf, buf->len-total_len);



    if(iphdr->protocol!=NET_PROTOCOL_ICMP&&iphdr->protocol!=NET_PROTOCOL_UDP)
        icmp_unreachable(buf, iphdr->src_ip, ICMP_CODE_PROTOCOL_UNREACH);
    else 
        {
        // rm header
        buf_remove_header(buf, hdr_len);
        net_in(buf, iphdr->protocol, iphdr->src_ip);
        }
    
}

/**
 * @brief 处理一个要发送的ip分片
 * 
 * @param buf 要发送的分片
 * @param ip 目标ip地址
 * @param protocol 上层协议
 * @param id 数据包id
 * @param ofs 分片ofs，必须被8整除
 * @param mf 分片mf标志，是否有下一个分片
 */
void ip_fragment_out(buf_t *buf, uint8_t *ip, net_protocol_t protocol, int id, uint16_t ofs, int mf)
{
    // TO-DO FIXEME
    buf_add_header(buf, sizeof(ip_hdr_t));
    ip_hdr_t *iphdr_out = (ip_hdr_t *)buf->data;

    // 填充IP报头
    iphdr_out->version = IP_VERSION_4;
    iphdr_out->protocol = protocol;
    iphdr_out->hdr_len = sizeof(ip_hdr_t) / IP_HDR_LEN_PER_BYTE;
    iphdr_out->tos = 0;
    iphdr_out->total_len16 = swap16(buf->len);
    iphdr_out->id16 = swap16(id);
    iphdr_out->flags_fragment16 = mf ? swap16(IP_MORE_FRAGMENT | (ofs >> 3)) : swap16(ofs >> 3);
    iphdr_out->ttl = IP_DEFALUT_TTL;
    memcpy(iphdr_out->dst_ip, ip, NET_IP_LEN);
    memcpy(iphdr_out->src_ip, net_if_ip, NET_IP_LEN);

    // 计算校验和
    iphdr_out->hdr_checksum16 = 0;
    iphdr_out->hdr_checksum16 = checksum16((uint16_t *)iphdr_out, sizeof(ip_hdr_t));
    // 发送
    arp_out(buf, ip);
    


}

/**
 * @brief 处理一个要发送的ip数据包
 * 
 * @param buf 要处理的包
 * @param ip 目标ip地址
 * @param protocol 上层协议
 */
void ip_out(buf_t *buf, uint8_t *ip, net_protocol_t protocol)
{
    // TO-DO
    
/*  废弃， 这么写有点重复，最后一片可以当做无需分片的处理
    // if(buf->len<=MAX_DATA_TRANSFER_SIZE){
    //     // 不分片发送
    //     ip_fragment_out(buf, ip, NET_PROTOCOL_ICMP, 0, 0, 0);
    // }
    // else{
    // // 分片发送
*/          
    unsigned int frag_sz = ETHERNET_MAX_TRANSPORT_UNIT - sizeof(ip_hdr_t);
    static int id = 0;
    unsigned int ofs = 0; // 已发送的分片数，实际字节偏移量需乘frag_sz
    buf_t ipbuf; // 如果直接初始化为buf_t * ip_buf; 会段错误, 可能是没有处理好对齐的关系
    
    while (buf->len > frag_sz) {
        // 如果数据段长度超过了eht_mtu就直接分片，分片长度取最大的mtu-header_size
        buf_init(&ipbuf, frag_sz);
        memcpy(ipbuf.data, buf->data, frag_sz);
        ip_fragment_out(&ipbuf, ip, protocol, id, ofs * frag_sz ,1);
        ofs++;
        // 剔除已发送的分段(这个地方忘了会出大问题，确信)
        buf_remove_header(buf, frag_sz);
    }
    // 发送无需分片的部分
    buf_init(&ipbuf, buf->len);
    memcpy(ipbuf.data, buf->data, buf->len);
    ip_fragment_out(&ipbuf, ip, protocol, id, ofs * frag_sz, 0);
    id++;

}

/**
 * @brief 初始化ip协议
 * 
 */
void ip_init()
{
    net_add_protocol(NET_PROTOCOL_IP, ip_in);
}